#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <experimental/filesystem>
#include <json/json.h>

#include <sys/stat.h>

#include "bundle.h"

/* struct stat {
    dev_t     st_dev;     // 文件所属设备 ID
    ino_t     st_ino;     // 文件 inode 编号
    mode_t    st_mode;    // 文件类型与权限
    nlink_t   st_nlink;   // 硬链接数量
    uid_t     st_uid;     // 文件所有者的用户 ID
    gid_t     st_gid;     // 文件所有者的组 ID
    dev_t     st_rdev;    // 如果是设备文件，则是设备编号
    off_t     st_size;    // 文件字节大小
    blksize_t st_blksize; // IO 块大小（文件系统建议的 block size）
    blkcnt_t  st_blocks;  // 实际占用磁盘块数（512 字节为单位）

    struct timespec st_atim; // 最后访问时间（access time）
    struct timespec st_mtim; // 最后修改时间（modification time）
    struct timespec st_ctim; // 最后状态更改时间（change time）
};
*/

namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class FileTool
    {
    private:
        std::string _FileName;

    public:
        FileTool(const std::string &FileName) : _FileName(FileName) {}

        // 获取文件名
        std::string getFileName()
        {
            auto pos = _FileName.rfind("/");
            if (pos == std::string::npos)
            {
                return _FileName;
            }
            return _FileName.substr(pos + 1);
        }

        // 获取文件大小
        int64_t getSize()
        {
            struct stat st;
            if (stat(_FileName.c_str(), &st) < 0)
            {
                std::cerr << "tool.hpp getSize error" << std::endl;
                return -1;
            }

            return st.st_size;
        }

        // 获取最后一次修改时间
        time_t getMTime()
        {
            struct stat st;
            if (stat(_FileName.c_str(), &st) < 0)
            {
                std::cerr << "tool.hpp getMTime error" << std::endl;
                return -1;
            }

            return st.st_mtim.tv_sec;
        }

        // 获取最后一次访问时间
        time_t getATime()
        {
            struct stat st;
            if (stat(_FileName.c_str(), &st) < 0)
            {
                std::cerr << "tool.hpp getATime error" << std::endl;
                return -1;
            }

            return st.st_atim.tv_sec;
        }

        // 获取文件从指定位置开始的内容
        bool getPosContent(std::string *content, size_t pos, size_t len)
        {
            size_t fsize = getSize();
            if (pos + len > fsize)
            {
                std::cerr << "tool.hpp getPosContent error" << std::endl;
                return false;
            }

            std::ifstream file;
            file.open(_FileName, std::ios::binary);
            if (file.fail())
            {
                std::cerr << "tool.hpp getPosContent open error" << std::endl;
                return false;
            }

            // 从文件开始位置偏移pos
            file.seekg(pos, std::ios::beg);
            content->resize(len);

            file.read(&(*content)[0], len);
            if (file.fail())
            {
                std::cerr << "tool.hpp getPosContent read error" << std::endl;
                return false;
            }

            return true;
        }

        // 获取文件全部内容
        bool getAllContent(std::string *content)
        {
            size_t fsize = getSize();
            return getPosContent(content, 0, fsize);
        }

        // 设置文件内容
        bool setContent(const std::string &content)
        {
            std::ofstream file;
            file.open(_FileName, std::ios::binary);
            if (file.fail())
            {
                std::cerr << "tool.hpp getAllContent open error" << std::endl;
                return false;
            }

            file.write(content.c_str(), content.size());
            if (file.fail())
            {
                std::cerr << "tool.hpp getAllContent write error" << std::endl;
                return false;
            }

            return true;
        }

        // 删除文件
        bool remove()
        {
            return fs::remove(_FileName);
        }

        // 压缩文件
        bool zip(const std::string &name)
        {
            std::string content;
            if (getAllContent(&content) == false)
            {
                std::cerr << "tool.hpp zip getAllContent error" << std::endl;
                return false;
            }

            std::string pack = bundle::pack(bundle::LZIP, content);

            FileTool newfile(name);
            if (newfile.setContent(pack) == false)
            {
                std::cerr << "tool.hpp zip setContent error" << std::endl;
                return false;
            }

            return true;
        }

        // 解压文件
        bool unzip(const std::string &name)
        {
            std::string content;
            if (getAllContent(&content) == false)
            {
                std::cerr << "tool.hpp unzip getAllContent error" << std::endl;
                return false;
            }

            std::string unpack = bundle::unpack(content);

            FileTool newfile(name);
            if (newfile.setContent(unpack) == false)
            {
                std::cerr << "tool.hpp unzip setContent error" << std::endl;
                return false;
            }

            return true;
        }

        // 判断文件是否存在
        bool exists()
        {
            return fs::exists(_FileName);
        }

        // 创建目录
        bool createDirectory()
        {
            if (exists())
                return true;

            return fs::create_directories(_FileName);
        }

        // 查看目录内文件
        bool scanDirectory(std::vector<std::string> *array)
        {
            for (auto &p : fs::directory_iterator(_FileName))
            {
                if (fs::is_directory(p))
                    continue;

                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    };

    // json序列化反序列化封装
    class jsonTool
    {
    public:
        // 序列化
        static bool serialize(const Json::Value &root, std::string *content)
        {
            Json::StreamWriterBuilder builder;
            builder.settings_["emitUTF8"] = true; // 禁用 Unicode 转义

            std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
            std::stringstream ss;

            if (writer->write(root, &ss) != 0)
            {
                std::cerr << "tool.hpp jsonTool serialize write error" << std::endl;
                return false;
            }
            *content = ss.str();
            return true;
        }

        // 反序列化
        static bool deserialize(const std::string &content, Json::Value *root)
        {
            Json::CharReaderBuilder builder;
            std::unique_ptr<Json::CharReader> reader(builder.newCharReader());

            std::string errors;
            return reader->parse(content.c_str(), content.c_str() + content.size(), root, &errors);
        }
    };
}